/* This file is part of BORG.
 * BORG is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *  
 * BORG is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *   
 * You should have received a copy of the GNU General Public License
 * along with BORG; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * Copyright 2003 by Mike Berger
 */

package net.sf.borg.model;

import java.io.File;
import java.io.Writer;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Vector;

import net.sf.borg.common.Errmsg;
import net.sf.borg.common.PrefName;
import net.sf.borg.common.Prefs;
import net.sf.borg.common.XTree;
import net.sf.borg.control.systemLogin.BorgNameLoginSystem;
import net.sf.borg.model.beans.Address;
import net.sf.borg.model.beans.AddressXMLAdapter;
import net.sf.borg.model.beans.Appointment;
import net.sf.borg.model.db.BeanDB;
import net.sf.borg.model.db.jdbc.AddrJdbcDB;
import net.sf.borg.model.undo.AddressUndoItem;
import net.sf.borg.model.undo.UndoLog;

/**
 * AddressModel class
 *
 * AddressModel contains functionality specific to the 
 * model used for BORG addresses.  
 *
 * @author Mike Berger
 * 
 * This class has been modified by UF Software Engineering Summer 2009
 * Developer: Dylan Moore
 */
public class AddressModel extends Model {

	private AddrJdbcDB addrDB_; // the address database

	public BeanDB<Address> getDB() {
		return (addrDB_);
	}

	private HashMap<Integer, LinkedList<Address>> bdmap_ = new HashMap<Integer, LinkedList<Address>>();

	static private AddressModel self_ = null;

	private void load_map() {

		// clear map
		bdmap_.clear();

		try {

			// iterate through tasks using taskmodel
			Collection<Address> addrs = getAddresses();
			for (Address addr : addrs) {

				if (addr.getDeleted())
					continue;

				// use birthday to build a day key
				Date bd = addr.getBirthday();
				if (bd == null)
					continue;

				GregorianCalendar g = new GregorianCalendar();
				g.setTime(bd);

				int key = AppointmentModel.dkey(g);
				int bdkey = AppointmentModel.birthdayKey(key);

				// add the task string to the btmap_
				// add the task to the mrs_ Vector. This is used by the todo gui
				LinkedList<Address> o = bdmap_.get(new Integer(bdkey));
				if (o == null) {
					o = new LinkedList<Address>();
					bdmap_.put(new Integer(bdkey), o);
				}

				o.add(addr);
			}

		} catch (Exception e) {

			Errmsg.errmsg(e);
			return;
		}

	}

	public static AddressModel getReference() {
		return (self_);
	}

	public static AddressModel create() {
		self_ = new AddressModel();
		return (self_);
	}

	public void remove() {
		removeListeners();
		try {
			if (addrDB_ != null)
				addrDB_.close();
		} catch (Exception e) {
			Errmsg.errmsg(e);
			return;
		}
		addrDB_ = null;
	}

	/**
    * This method removes a group of the specified name.  After this 
    * method has finished, any addresses previously associated with this group
    * name will no longer be associated to the given group name.
    * 
    * Method created by UF Software Engineering, Summer 2009
    * Developer: Dylan Moore
    * @param groupName the name of the group to remove
	*/
	public void removeGroup(String groupName)
	{
		try {
			addrDB_.removeGroup(groupName);
		} catch (SQLException e) {
			Errmsg.errmsg(e);
		}
	}

	public Collection<Address> getAddresses() throws Exception {
		Collection<Address> addrs = addrDB_.readAll();
		return addrs;
	}

	public Collection<Address> getAddresses(int daykey) {
		// don't consider year for birthdays
		int bdkey = AppointmentModel.birthdayKey(daykey);
		// System.out.println("bdkey is " + bdkey);
		return (bdmap_.get(new Integer(bdkey)));
	}

	// open the SMDB database
	@SuppressWarnings("unchecked")
	public void open_db(String url) throws Exception {
		addrDB_ = new AddrJdbcDB(url);
		load_map();
	}
	
	public void delete(int key) {
		try {
			Address addr = getAddress(key);
			if (addr != null)
				delete(addr);
		} catch (Exception e) {
			Errmsg.errmsg(e);
		}
	}

	public void delete(Address addr) {
		delete(addr, false);
	}

	/**
    * Deletes an address.
    * 
    * Method edited by UF Software Engineering, Summer 2009
    * Developer: Dylan Moore
    * Change Summary: Added method call to remove address picture.
    * @param addr
    * @param undo
	*/
	public void delete(Address addr, boolean undo) {

		try {
			Address orig_addr = getAddress(addr.getKey());
			LinkModel.getReference().deleteLinks(addr);

			addrDB_.delete(addr.getKey());
			
			/* UF SE 2009 - Start of Additions */
			deletePicture(addr);
			/* UF SE 2009 - End of Additions */
			
			if (!undo) {
				UndoLog.getReference().addItem(
						AddressUndoItem.recordDelete(orig_addr));
			}

		} catch (Exception e) {
			Errmsg.errmsg(e);
		}

		refresh();
	}

	/**
	 * Deletes a picture associated with the given Address
	 * 
	 * Method created by UF Software Engineering, Summer 2009
	 * Developer: Dylan Moore
	 * @param addr picture associated with this Address will be deleted
	 */
	private void deletePicture(Address addr) {
		//get the picture path
		String contactPicsPath =  Prefs.getPref(PrefName.HSQLDBDIR) 
			+ File.separator + BorgNameLoginSystem.getInstance().getUsername() +
			File.separator + Prefs.getPref(PrefName.CONTACTPHOTOSDIR) + File.separator;
		String fullPicPath = contactPicsPath + addr.getKey() + ".jpg";
		File picFile;
		
		//if the picture exists and is a file, delete it
		if ((picFile = new File(fullPicPath)).exists() && !picFile.isDirectory()) {
			picFile.delete();
		}
	}

	public void saveAddress(Address addr) {
		saveAddress(addr, false);
	}

	public void saveAddress(Address addr, boolean undo) {

		int num = addr.getKey();
		try {
			Address orig_addr = getAddress(addr.getKey());
			if (num == -1 || orig_addr == null) {

				int newAddrKey = addrDB_.nextkey(); //get addr key
				if (undo && num != -1 && orig_addr == null)
				{
					newAddrKey = num;
				}
				addr.setKey(newAddrKey);
				addr.setNew(true);
				addrDB_.addObj(addr);
				if (!undo) {
					UndoLog.getReference().addItem(
							AddressUndoItem.recordAdd(addr));
				}
			} else {
				addr.setModified(true);
				addrDB_.updateObj(addr);
				if (!undo) {
					UndoLog.getReference().addItem(
							AddressUndoItem.recordUpdate(orig_addr));
				}
			}
		} catch (Exception e) {
			Errmsg.errmsg(e);
		}

		// inform views of data change
		refresh();
	}

	/**
	 * Saves a group name.  Note that this group will not have any Addresses associated with it.
	 * 
	 * Method created by UF Software Engineering, Summer 2009
	 * Developer: Dylan Moore
	 * @param groupName
	 */
	public void saveGroup(String groupName)
	{
		try {
			addrDB_.addGroup(groupName);
		} catch (Exception e) {
			Errmsg.errmsg(e);
		}
	}

	/**
	 * Returns all Address's associated with a given group name.
	 * 
	 * Method created by UF Software Engineering, Summer 2009
	 * Developer: Dylan Moore
	 * @param groupName
	 * @return ArrayList<Address> 
	 */
	public ArrayList<Address> getAddressesByGroup(String groupName)
	{
		ArrayList<Address> ret = new ArrayList<Address>();
		try {
			ret = addrDB_.getAddressByGroup(groupName);
		} catch (SQLException e) {
			Errmsg.errmsg(e);
		}
		return ret;
	}

	/**
	 * This method returns all distinct groups.
	 * 
	 * Method created by UF Software Engineering, Summer 2009
	 * Developer: Dylan Moore
	 * @return ArrayList<Address> 
	 */
	public ArrayList<String> getGroups()
	{
		ArrayList<String> ret = new ArrayList<String>();
		try {
			ret = addrDB_.getAllGroups();
		} catch (SQLException e) {
			Errmsg.errmsg(e);
		}
		return ret;
	}

	// allocate a new Row from the DB
	public Address newAddress() {
		return (addrDB_.newObj());
	}

	public Address getAddress(int num) throws Exception {
		return (addrDB_.readObj(num));
	}

	public void export(Writer fw) throws Exception {

		// FileWriter fw = new FileWriter(fname);
		fw.write("<ADDRESSES>\n");
		AddressXMLAdapter ta = new AddressXMLAdapter();

		// export addresses

		Collection<Address> addrs = getAddresses();
		for (Address addr : addrs) {

			XTree xt = ta.toXml(addr);
			fw.write(xt.toString());
		}

		fw.write("</ADDRESSES>");

	}

	public void importXml(XTree xt) throws Exception {

		AddressXMLAdapter aa = new AddressXMLAdapter();

		for (int i = 1;; i++) {
			XTree ch = xt.child(i);
			if (ch == null)
				break;

			if (!ch.name().equals("Address"))
				continue;
			Address addr = aa.fromXml(ch);
			addr.setKey(-1);
			saveAddress(addr);
		}

		refresh();
	}

	public void refresh() {
		load_map();
		refreshListeners();
	}

	public void sync() {
		addrDB_.sync();
		refresh();
	}    
		/**
	    * This method searches the AddressModel for an Address by first name and last name
	    *
	    * Method created by UF Software Engineering Summer 2009
	    * Developer: Gonzalo Medina, Darren Goldfarb
	    * @param s the string which is being search for
	    * @param case_sensitive a boolean of whether the search is case sensitive or not
	    */ 
	
	public Vector<Address> get_srch(String s, boolean case_sensitive) {

		Vector<Address> res = new Vector<Address>();
		
		try {

			// load all addresses/contacts into contact list from AddressModel.java
			Collection<Address> alladdr = getAddresses();
			
			for (Address addr : alladdr) {
			// read each addr (Address.java)

				
				String fn = addr.getFirstName();
				String ln = addr.getLastName();
				if (fn == null || ln == null)
					continue;
	
				if (case_sensitive) {
					// check if addr text contains the search string
					if ( fn.indexOf(s)== -1 && ln.indexOf(s)== -1) 
					//Returns the index within this string of the first occurrence of the specified substring.
						continue;
						
				} else {
					// check if addr text contains the search string
					String lfn = fn.toLowerCase();
					String lln = ln.toLowerCase();
					String ls = s.toLowerCase();
					if (lfn.indexOf(ls)== -1 && lln.indexOf(ls) == -1)
						continue;
				}	

				// add the addr to the search results
				res.add(addr);	
				
			}

		} catch (Exception e) {
			Errmsg.errmsg(e);
		}
		return (res);

	}
}
